// Copyright (C) 2008 Taylor L. Riche <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

package frontend;


import appcomp.*;
import appcomp.entities.*;
import syscomp.*;
import core.*;
import profcomp.*;
import profcomp.entities.*;
import appcomp.pathfinder.*;
import appcomp.graphtools.*;
import appcomp.graphtools.LagniappeGraph.CompressionPolicy;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.io.FileReader;
import java.lang.Exception;
import com.javadude.antxr.scanner.BasicCrimsonXMLTokenStream;

/**
 * @author riche
 *
 */
public class Main {

	/**
	 * Main starting method of application. Will compile system model or 
	 * programming model based on option switches given in args.
	 * @param args The option switch and file name to be compiled.
	 */
	public static void main(String[] args) throws Exception {
		
		BasicCrimsonXMLTokenStream stream = null;
		
		if (args[0].equals("--app")) {
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
				System.exit(1);
			}
			// Create AppParser
			try {
				AppParser appParser = new AppParser(stream);
				appParser.document(ParserFunc.GENCODE);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
				System.exit(1);
			}
		}
//		else if (args[0].equals("--graph")) {
//			String appName = null;
//			try {
//				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
//			}
//			catch (Exception e){
//				System.err.println(e.getMessage());
//				System.exit(1);
//			}
//			// Create AppParser
//            try {
//                AppParser appParser = new AppParser(stream);
//                appParser.document(ParserFunc.GENCODE);
//            }
//            catch (Exception e) {
//                System.err.println(e.getMessage());
//                System.exit(1);
//            }
//        }
        else if (args[0].equals("--graph")) {
            String appName = null;
            try {
                stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
            }
            catch (Exception e){
                System.err.println(e.getMessage());
                System.exit(1);
            }
            // Create AppParser

			AppParser appParser = new AppParser(stream);
			try {
				appName = appParser.document(ParserFunc.GETENTS);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
				System.exit(1);
			}
			LagniappeGraph lg = new LagniappeGraph();
			// prune non-data-path connections
//			System.out.println("Original ops:");
//            for(Operator o : appParser.getOpList()) {
//                System.out.println("\t" + o.getName());
//            }
			lg.formGraph(appParser.getOpList(), new ArrayList<Connector>(appParser.getConList()));
			lg.setPolicy(CompressionPolicy.Greedy);
			List<Operator> collector = lg.compressGraph();
//			System.out.println("Printing new operator names:");
			for(Operator o : collector) {
                if(o instanceof Composite) {
                    ((Composite) o).setCompositeName();
                }
//				System.out.println("\t" + o.getName());
			}
			for(Operator o: appParser.getOpList()) {
			    if(o.doesGenerate() && !collector.contains(o)) {
			        collector.add(o);
			    }
			}
			System.out.println();
			ArrayList<Connector> newList = lg.consumeConnectors(collector, appParser.getConList());
            ArrayList<Connector> purgeList = new ArrayList<Connector>();
            for(Connector c : appParser.getConList()) {
                if(c.getOut().isInComposite() || c.getIn().isInComposite()) {
                    purgeList.add(c);
                }
            }
            for(Connector c : purgeList) {
                appParser.getConList().remove(c);
            }
            for(Connector c : newList) {
                appParser.getConList().add(c);
                Operator inO = c.getIn();
                Port inP = inO.getPortByName(c.getInName().getRight());
                inP.setConn(c);
                Operator outO = c.getOut();
                Port outP = outO.getPortByName(c.getOutName().getRight());
                outP.setConn(c);
            }
			for(Connector c : appParser.getConList()) {
				System.out.println(c.prettyString());
			}
			System.out.println();
			for(Operator o : collector) {
				o.printPorts();
			}
			int newIndex = 0;
			for(Operator o : collector) {
			    o.setIndex(newIndex);
			    newIndex++;
			}
			
			try {
			    stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), SysParser.class, false, false);
			}
			catch (Exception e) {
			    System.err.println(e.getMessage());
			    System.exit(1);
			}

	         // Create SysParser
			String sysName = null;
            try {
                SysParser sysParser = new SysParser(stream);
                sysName = sysParser.document(ParserFunc.GETNAME);
            }
            catch (Exception e) {
                System.err.println(e.getMessage());
                e.printStackTrace();
                System.exit(1);
            }

            CodeGenerator cg = new CodeGenerator();
            cg.registerEntities(collector);
            cg.generateEntities(collector);
            cg.generateApplication(appName, null, collector, appParser.getConList());
            cg.generateProfiler(appName, sysName, collector);
		}
		else if (args[0].equals("--final")) {
			Map<String, OperatorInfo> oiMap = null;
			try {
			    if(!args[1].equals("--nograph")) {
			        stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), ProfParser.class, false, false);
			    }
			    else {
                    stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), ProfParser.class, false, false);
			    }
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			ProfParser profParser = new ProfParser(stream);
			try {
				oiMap = profParser.document();
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			for(OperatorInfo oi : oiMap.values()) {
				System.out.println(oi.getName());
				oi.printPorts();
			}
			String appName = null, sysName = null;
			try {
				if (!args[1].equals("--nograph")) {
                    stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), AppParser.class, false, false);
                }
				else {
                    stream = new BasicCrimsonXMLTokenStream(new FileReader(args[3]), AppParser.class, false, false);				    
				}
			}
			catch (Exception e){
				System.err.println(e.getMessage());
				System.exit(1);
			}
            // Create AppParser

            AppParser appParser = new AppParser(stream);
            try {
                appName = appParser.document(ParserFunc.GETENTS);
            }
            catch (Exception e) {
                System.err.println(e.getMessage());
                e.printStackTrace();
                System.exit(1);
            }
            List<Operator> collector = null; 
            if(args.length > 1 && args[1].equals("--nograph")) {
                collector = appParser.getOpList();
            }
            else {
                LagniappeGraph lg = new LagniappeGraph();
                lg.formGraph(appParser.getOpList(), new ArrayList<Connector>(appParser.getConList()));
                lg.setPolicy(CompressionPolicy.Greedy);
                collector = lg.compressGraph();
                for(Operator o : collector) {
                    if(o instanceof Composite) {
                        ((Composite) o).setCompositeName();
                    }
                }
                for(Operator o: appParser.getOpList()) {
                    if(o.doesGenerate() && !collector.contains(o)) {
                        collector.add(o);
                    }
                }
                ArrayList<Connector> newList = lg.consumeConnectors(collector, appParser.getConList());
                ArrayList<Connector> purgeList = new ArrayList<Connector>();
                for(Connector c : appParser.getConList()) {
                    if(c.getOut().isInComposite() || c.getIn().isInComposite()) {
                        purgeList.add(c);
                    }
                }
                for(Connector c : purgeList) {
                    appParser.getConList().remove(c);
                }
                for(Connector c : newList) {
                    appParser.getConList().add(c);
                    Operator inO = c.getIn();
                    Port inP = inO.getPortByName(c.getInName().getRight());
                    inP.setConn(c);
                    Operator outO = c.getOut();
                    Port outP = outO.getPortByName(c.getOutName().getRight());
                    outP.setConn(c);
                }
                for(Connector c : appParser.getConList()) {
                    System.out.println(c.prettyString());
                }
                int newIndex = 0;
                for(Operator o : collector) {
                    o.setIndex(newIndex);
                    newIndex++;
                }
            }
			
			for (Operator o : collector) {
				if(!o.doesGenerate()) {
					String opName = o.getName();
					OperatorInfo opInfo = oiMap.get(opName);
					for (Port p : o.getPortList()) {
						if(p.getDirection() == Port.Direction.IN && p.getPortVis() == Port.Visibility.EXTERNAL) {
							PortInfo pi = opInfo.lookupPort(Integer.toString(p.getIndex()));
							o.setDelay(p.getName(), pi.getValue());
						}
					}
				}
			}
			Pathfinder pf = new Pathfinder();
			int listSize = collector.size();
			for (Operator o : collector) {
				System.out.print(o.getName());
				if(o.doesGenerate()) {
					int pathcount = pf.findPaths(o);
					System.out.println(pathcount);
				}
				else {
					System.out.println();
				}
			}
			pf.printPaths();
			for(Path p : pf.getMasterPathList()) {
				double slack = p.getSlack(appParser.getDelayGuarantee());
				p.distributeSlack(slack);
			}
			for(Operator o : collector) {
				o.translateSlack();
			}
			CodeGenerator cg = new CodeGenerator();
			cg.registerEntities(collector);
			cg.generateEntities(collector);
		}
		else if (args[0].equals("--prof")) {
			String appName = null, sysName = null;
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
			}
			catch (Exception e){
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create AppParser

			AppParser appParser = new AppParser(stream);
			try {
				appName = appParser.document(ParserFunc.GETNAME);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
				System.exit(1);
			}
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), SysParser.class, false, false);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create SysParser

			try {
				SysParser sysParser = new SysParser(stream);
				sysName = sysParser.document(ParserFunc.GETNAME);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
				System.exit(1);
			}
			CodeGenerator cg = new CodeGenerator();
			//cg.generateMain(appName, sysName);
			cg.generateProfiler(appName, sysName, appParser.getOpList());
		}
		else if (args[0].equals("--sys")) {
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), SysParser.class, false, false);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create AppParser

			try {
				SysParser sysParser = new SysParser(stream);
				sysParser.document(ParserFunc.GENCODE);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
		}
		else if (args[0].equals("--main")) {
			String appName = null, sysName = null;
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
			}
			catch (Exception e){
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create AppParser

			try {
				AppParser appParser = new AppParser(stream);
				appName = appParser.document(ParserFunc.GETNAME);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), SysParser.class, false, false);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create SysParser

			try {
				SysParser sysParser = new SysParser(stream);
				sysName = sysParser.document(ParserFunc.GETNAME);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			CodeGenerator cg = new CodeGenerator();
			cg.generateMain(appName, sysName);
		}
		else if (args[0].equals("--all")) {
			String appName = null, sysName = null;
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[1]), AppParser.class, false, false);
			}
			catch (Exception e){
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create AppParser
			try {
				AppParser appParser = new AppParser(stream);
				appName = appParser.document(ParserFunc.GENCODE);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			try {
				stream = new BasicCrimsonXMLTokenStream(new FileReader(args[2]), SysParser.class, false, false);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			// Create AppParser

			try {
				SysParser sysParser = new SysParser(stream);
				sysName = sysParser.document(ParserFunc.GENCODE);
			}
			catch (Exception e) {
				System.err.println(e.getMessage());
				System.exit(1);
			}
			CodeGenerator cg = new CodeGenerator();
			cg.generateMain(appName, sysName);
		}
		else {
			System.err.println("Command not found!");
			System.exit(1);
		}
	}

}
